package org.zengrong.display.spritesheet {
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;
	import org.zengrong.utils.ObjectUtil;

	/**
	 * 处理SpriteSheet的元数据
	 * @author zrong
	 */
	public class SpriteSheetMetadata {
		public function SpriteSheetMetadata(){
			
		}
		public var type:String;       //Sheet的类型，见SpriteSheetType
		public var hasLabel:Boolean;  //Label信息用于分段动画
		public var hasName:Boolean;   //每一帧都使用了名称
		/**
		 * mask的类型，详见org.zengrong.display.spritesheet.MaskType。mask信息只能存在于JPG文件中
		 */
		public var maskType:int;
		public var labels:Vector.<String>;
		/**
		 * 每个Label的的帧范围，键名是Label，键值是一个数组，第一个元素为开始帧（0基），第二个元素为该Label总帧数
		 */
		public var labelsFrame:Object;
		public var names:Vector.<String>; //名称来指定每一帧，则这里保存所有名字的数组
		public var namesIndex:Object;     //保存每个名称与帧的对应关系
		/**
		 * 每帧在整个大的Sheet中的位置和尺寸
		 * 这并非是最终在大的Sheet中体现出来的位置和尺寸。而是是未修剪之前的尺寸。也就是最终要在程序中使用的尺寸。
		 * 因为如果执行了修剪，那么在大的Sheet中只会保存有效像素（空白的边缘被修建掉了）。
		 * 而在还原到程序中的时候，还是必须使用原来的Rect大小的（因为要考虑到动作的外延范围）。
		 * 这个Vector中保存的始终都是在程序使用时候的Rect。
		 */
		public var frameRects:Vector.<Rectangle>;
		/**
		 * 每帧原始的Rect，基于frameRect保存修剪信息
		 * 例如，对frameRect进行了修剪操作，修剪的上下左右的值均为10像素。修剪后的frameRect为(0,0,50,50)
		 * 那么originalFrameRect则为(-10,-10,70,70)
		 * 如果frameRect没有经过修剪，则这个rect的w和h值与frameRect中的对应元素相等，但x和y为0
		 */
		public var originalFrameRects:Vector.<Rectangle>;
		
		/**Sheet的帧数*/
		public function get totalFrame():int {
			return frameRects ? frameRects.length : 0;
		}

		/**返回自身的副本（深复制）*/
		public function clone():SpriteSheetMetadata {
			var _meta:SpriteSheetMetadata = new SpriteSheetMetadata();
			_meta.type     = type;
			_meta.hasLabel = hasLabel;
			_meta.hasName  = hasName;
			_meta.maskType = maskType;
			if (frameRects){
				_meta.frameRects = new Vector.<Rectangle>;
				for (var i:int = 0; i < frameRects.length; i++){
					_meta.frameRects[i] = frameRects[i].clone();
					_meta.originalFrameRects[i] = originalFrameRects[i].clone();
				}
			}
			if (labels)
				_meta.labels = labels.concat();
			if (labelsFrame)
				_meta.labelsFrame = ObjectUtil.clone(labelsFrame);
			if (names)
				_meta.names = names.concat();
			if (namesIndex)
				_meta.namesIndex = ObjectUtil.clone(namesIndex);
			return _meta;
		}

		/**销毁整个对象*/
		public function destroy():void {
			type = null;
			hasLabel = false;
			maskType = 0;
			hasName = false;
			labels = null;
			labelsFrame = null;
			names = null;
			namesIndex = null;
			frameRects = null;
		}

		/**
		 * 根据设置的属性初始化一些值
		 * @param $force 是否强制初始化。值为true则不判断原来是否存在该变量，强行覆盖
		 */
		protected function setup($force:Boolean = false):void {
			var _frameRects:Vector.<Rectangle> = new Vector.<Rectangle>;
			var _originalFrameRects:Vector.<Rectangle> = new Vector.<Rectangle>;
			if ($force){
				frameRects = _frameRects;
				originalFrameRects = _originalFrameRects;
			} else if (!frameRects){
				frameRects = _frameRects;
				originalFrameRects = _originalFrameRects;
			}
		}

		/**
		 * 增加一个Label
		 * @param $labelName 要增加的Label的名称
		 * @param $labelFrame 要增加的Label的帧索引
		 *
		 * @throw TypeError 提供的Label名称或者帧索引为空的时候抛出错误
		 */
		public function addLabel($labelName:String, $labelFrame:Array):void {
			if (!$labelName)
				throw new TypeError('增加的Label不能为空！');
			if (!$labelFrame || $labelFrame.length == 0)
				throw new TypeError('必须为增加的Label(' + $labelName + ')定义帧序列！');
			hasLabel = true;
			if (!labels)
				labels = new Vector.<String>;
			if (!labelsFrame)
				labelsFrame = {};
			labels[labels.length] = $labelName;
			labelsFrame[$labelName] = $labelFrame;
		}

		/**
		 * 设置Label的属性
		 * @param $hasLabel	是否使用了Label
		 * @param $labels	Label的对象，每个键名为label名称，每个键值是数组，保存帧的索引号，格式为:[1,2,3]
		 *
		 */
		public function setLabels($hasLabel:Boolean, $labels:Object = null):void {
			trace('SpriteSheetMetadata.setLabels:', $hasLabel, $labels);
			//必须传递可用的$items才算是使用了Label，否则都算没有Label
			if ($hasLabel && $labels){
				hasLabel = true;
				labels = new Vector.<String>;
				labelsFrame = ObjectUtil.clone($labels);
				for (var _labelName:String in $labels){
					labels[labels.length] = _labelName;
				}
			} else {
				hasLabel = false;
				labels = null;
				labelsFrame = null;
			}
		}

		/**
		 * 从外部向数组中添加帧的尺寸，一般在循环中执行
		 */
		public function addFrame($sizeRect:Rectangle, $originalRect:Rectangle = null, $name:String = null):void {
//		if(frameRects.length>=frameCount)
//			return;
			setup();
			writeFrame(frameRects.length, $sizeRect, $originalRect, $name);
		}

		public function addFrameAt($index:int, $sizeRect:Rectangle, $originalRect:Rectangle = null, $name:String = null):void {
			setup();
			writeFrame($index, $sizeRect, $originalRect, $name);
		}

		public function removeFrameAt($index:int):void {
			frameRects.splice($index, 1);
			originalFrameRects.splice($index, 1);
			if (names && namesIndex){
				var _delName:String = names.splice($index, 1)[0] as String;
				//删除被删除的帧的名称
				delete namesIndex[_delName];
				for (var _name:String in namesIndex){
					//將所有帧编号大于被删除帧的帧编号减一
					if (namesIndex[_name] > $index)
						namesIndex[_name]--;
				}
			}
		}

		protected function writeFrame($index:int, $sizeRect:Rectangle, $originalRect:Rectangle = null, $name:String = null):void {
			if (!$originalRect)
				$originalRect = new Rectangle(0, 0, $sizeRect.width, $sizeRect.height);
			frameRects[$index] = $sizeRect;
			originalFrameRects[$index] = $originalRect;
			//trace('增加帧：', $index, $name);
			if ($name && names && namesIndex){
				names[$index] = $name;
				namesIndex[$name] = $index;
			}
		}

		//----------------------------------------
		// decode
		//----------------------------------------
		/**
		 * 从XML文件解析Metadata数据，XML文件必须由Sprite Sheet Editor生成。
		 * @param $xml 由Sprite Sheet Editor生成的XML文件
		 * 或者自行生成且符合Sprite Sheet Editor格式的XML文件。
		 */
		public function decodeFromXML($xml:XML):void {
			var i:int = 0;
			type = $xml.sheetType.toString();
			hasLabel = $xml.hasLabel.toString() == 'true';
			hasName = $xml.hasName.toString() == 'true';
			maskType = int($xml.maskType.toString());
			var _totalFrame:int = int($xml.totalFrame.toString());
			setup(true);
			var _frames:XMLList = $xml.frames.children();
			var _frame:XML = null;
			if (hasName){
				names = new Vector.<String>(_totalFrame);
				namesIndex = {};
			}
			var _frameRect:Rectangle = null;
			var _originalRect:Rectangle = null;
			for (i = 0; i < _totalFrame; i++){
				_frame = _frames[i];
				_frameRect = new Rectangle(int(_frame.x.toString()), int(_frame.y.toString()), int(_frame.w.toString()), int(_frame.h.toString()));
				_originalRect = new Rectangle(int(_frame.ox.toString()), int(_frame.oy.toString()), int(_frame.ow.toString()), int(_frame.oh.toString()));
				//如果没有提供原始Frame的值，就交给writeFrame自动计算
				if (_originalRect.x == 0 && _originalRect.y == 0 && _originalRect.width == 0 && _originalRect.height == 0)
					_originalRect = null;
				writeFrame(i, _frameRect, _originalRect);
				if (hasName){
					names[i] = _frame.@name.toString();
					namesIndex[names[i]] = i;
				}
			}
			if (hasLabel){
				var _count:int = $xml.labels.@count;
				var _labelsXML:XMLList = $xml.labels.children();
				labels = new Vector.<String>(_count);
				labelsFrame = {};
				for (i = 0; i < _count; i++){
					labels[i] = XML(_labelsXML[i]).localName().toString();
					var _labelFrame:Array = _labelsXML[i].toString().split(',');
					//转换字符串为数字
					for (var j:int = 0; j < _labelFrame.length; j++){
						_labelFrame[j] = int(_labelFrame[j]);
						//处理第一帧小于0的情况
						if (j == 0 && _labelFrame[j] < 0)
							_labelFrame[0] = 0;
					}
					labelsFrame[labels[i]] = _labelFrame;
				}
			}
		}

		/**
		 * 从普通Object文件解析Metadata数据，Object必须由Sprite Sheet Editor生成的JSON格式Metadata解析而来。
		 */
		public function decodeFromObject($obj:Object):void {
			var i:int = 0;
			type = $obj.sheetType.toString();
			hasLabel = $obj.hasLabel;
			hasName = $obj.hasName;
			maskType = int($obj.maskType);
			var _totalFrame:int = $obj.totalFrame;
			setup(true);
			var _frames:Array = $obj.frames;
			var _frame:Object = null;
			if (hasName){
				names = new Vector.<String>(_totalFrame, true);
				namesIndex = {};
			}
			var _frameRect:Rectangle = null;
			var _originalRect:Rectangle = null;
			for (i = 0; i < _totalFrame; i++){
				_frame = _frames[i];
				_frameRect = new Rectangle(_frame.x, _frame.y, _frame.w, _frame.h);
				_originalRect = new Rectangle(_frame.ox, _frame.oy, _frame.ow, _frame.oh);
				//如果没有提供原始Frame的值，就交给writeFrame自动计算
				if (_originalRect.x == 0 && _originalRect.y == 0 && _originalRect.width == 0 && _originalRect.height == 0)
					_originalRect = null;
				writeFrame(i, _frameRect, _originalRect);
				if (hasName){
					names[i] = _frame.name;
					namesIndex[names[i]] = i;
				}
			}
			if (hasLabel){
				var _count:int = $obj.labels.count;
				labels = new Vector.<String>;
				labelsFrame = {};
				for each (var _labelName:String in $obj.labels){
					//count是一个特殊的属性，保存label的数量
					if (_labelName != 'count')
						continue;
					labels[labels.length] = _labelName;
					labelsFrame[_labelName] = $obj.labels[_labelName];
				}
			}
		}

		/**
		 * 从字节数组解析普通Object文件解析Metadata数据，ByteArray是从SS格式中提取的
		 * @param $ba 从SS格式中提取的Metadata数据
		 */
		public function decodeFromByteArray($ba:ByteArray):void {
		}

		//----------------------------------------
		// encode
		//----------------------------------------
		/**
		 * 返回一个标准的Object对象
		 * @param $isSimple 是否简单数据
		 */
		public function toObject($isSimple:Boolean = false, $includeName:Boolean = true):Object {
			var _jsonObj:Object = {frames: []};
			var _name:String = null;
			for (var i:int = 0; i < totalFrame; i++){
				_name = getFrameName($includeName, i);
				_jsonObj.frames[i] = getRectObject(frameRects[i], originalFrameRects[i], _name);
			}
			//加入附加信息
			if (!$isSimple){
				var _addObj:Object = getAddObject($includeName);
				for (var _addKey:String in _addObj)
					_jsonObj[_addKey] = _addObj[_addKey];
			}
			return _jsonObj;
		}

		/**
		 * 返回XML格式的Metadata
		 * @param $isSimple 是否简单数据
		 */
		public function toXML($isSimple:Boolean = false, $includeName:Boolean = true):XML {
			var _xml:XML =  <metadata />;var _frames:XML =  <frames />;var i:int = 0;
			var _name:String = null;
			for (i = 0; i < totalFrame; i++){
				_name = getFrameName($includeName, i);
				_frames.appendChild(getRectXML(frameRects[i], originalFrameRects[i], _name));
			}
			_xml.appendChild(_frames);
			if (!$isSimple){
				var _addXMLList:XMLList = getAddXML($includeName).children();
				for (i = 0; i < _addXMLList.length(); i++){
					_xml.appendChild(_addXMLList[i]);
				}
			}
			return _xml;
		}

		/**
		 * 返回Metadata的XML格式字符串，并加上XML头
		 * @param $isSimple 是否简单数据
		 */
		public function toXMLString($isSimple:Boolean = false, $includeName:Boolean = true, $lineEnding:String = '\n'):String {
			return getTextLine('<?xml version="1.0" encoding="UTF-8"?>', null, $lineEnding) + toXML($isSimple, $includeName).toXMLString();
		}

		/**
		 * 返回TXT格式的Metadata
		 * @param $isSimple 是否简单数据
		 */
		public function toTXT($isSimple:Boolean = false, $includeName:Boolean = true, $lineEnding:String = "\n"):String {
			var _str:String = getTextLine('frames', null, $lineEnding);
			var _name:String = null;
			for (var i:int = 0; i < totalFrame; i++){
				_name = getFrameName($includeName, i);
				_str += getRectTxt(frameRects[i], originalFrameRects[i], _name, $lineEnding);
			}
			//如果需要附加信息，要在帧信息前面加上frames字样
			if (!$isSimple)
				_str += getAddTXT($includeName, $lineEnding);
			return _str;
		}

		/**
		 * 获取Object格式的附加信息
		 */
		public function getAddObject($includeName:Boolean):Object {
			var _jsonObj:Object = {};
			//写入sheet的类型
			_jsonObj.sheetType = type;
			_jsonObj.hasLabel = hasLabel;
			_jsonObj.maskType = maskType;
			_jsonObj.hasName = $includeName;
			_jsonObj.totalFrame = totalFrame;
			if (hasLabel){
				_jsonObj.labels = ObjectUtil.clone(labelsFrame);
				_jsonObj.labels.count = labels.length;
			}
			return _jsonObj;
		}

		/**
		 * 获取XML格式的附加信息
		 */
		public function getAddXML($includeName:Boolean):XML {
			var _xml:XML =  <metadata />;
			_xml.sheetType = type;
			_xml.hasLabel = hasLabel;
			_xml.maskType = maskType;
			_xml.hasName = $includeName;
			_xml.totalFrame = totalFrame;
			if (hasLabel){
				var _labelXML:XML =  <labels />;
				_labelXML.@count = labels.length;
				for (var _key:String in labelsFrame){
					_labelXML[_key] = labelsFrame[_key].toString();
				}
				_xml.appendChild(_labelXML);
			}
			return _xml;
		}

		/**
		 * 获取TXT格式的附加信息
		 */
		public function getAddTXT($includeName:Boolean, $lineEnding:String = "\n"):String {
			var _str:String = '';
			_str += getTextLine('sheepType', type, $lineEnding);
			_str += getTextLine('hasLabel', hasLabel, $lineEnding);
			_str += getTextLine('maskType', maskType, $lineEnding);
			_str += getTextLine('hasName', $includeName, $lineEnding);
			_str += getTextLine('totalFrame', totalFrame, $lineEnding);
			if (hasLabel){
				_str += getTextLine('labels', null, $lineEnding);
				_str += getTextLine('count', labels.length, $lineEnding);
				for (var _key:String in labelsFrame){
					_str += getTextLine(_key, labelsFrame[_key].toString(), $lineEnding);
				}
			}
			return _str;
		}

		/**
		 * 返回Frame的Rect的Json格式
		 */
		public static function getRectObject($sizeRect:Rectangle, $originRect:Rectangle, $name:String = null):Object {
			var _obj:Object = {x: $sizeRect.x, y: $sizeRect.y, w: $sizeRect.width, h: $sizeRect.height, ox: $originRect.x, oy: $originRect.y, ow: $originRect.width, oh: $originRect.height};
			if ($name)
				_obj.name = $name;
			return _obj;
		}

		/**
		 * 返回Frame的Rect的XML格式
		 */
		public function getRectXML($sizeRect:Rectangle, $originRect:Rectangle, $name:String = null):XML {
			var _xml:XML =  <frame />;
			if($name)
				_xml.@name = $name;
			_xml.x = $sizeRect.x;
			_xml.y = $sizeRect.y;
			_xml.w = $sizeRect.width;
			_xml.h = $sizeRect.height;
			_xml.ox = $originRect.x;
			_xml.oy = $originRect.y;
			_xml.ow = $originRect.width;
			_xml.oh = $originRect.height;
			return _xml;
		}

		/**
		 * 返回Frame的Rect的纯文本格式
		 */
		public function getRectTxt($sizeRect:Rectangle, $originRect:Rectangle, $name:String = null, $lineEnding:String = "\n"):String {
			var _str:String = $name ? ($name + '=') : '';
			return _str + $sizeRect.x + ',' + $sizeRect.y + ',' + $sizeRect.width + ',' + $sizeRect.height + ',' + $originRect.x + ',' + $originRect.y + ',' + $originRect.width + ',' + $originRect.height + $lineEnding;
		}

		/**
		 * 返回键名+等号+键值+换行符格式的字符串。若不提供键值，则不加入等号
		 * @param $key	键名
		 * @param $value	键值
		 */
		private function getTextLine($key:String, $value:* = null, $lineEnding:String = "\n"):String {
			var _str:String = $key;
			if ($value != null)
				_str += '=' + $value.toString();
			return _str + $lineEnding;
		}

		/**
		 * 返回帧索引对应的帧名称。如果选择了不保存名称，或者没有名称，或者该帧没有对应名称，就返回null
		 * @param $includeName 是否包含名称
		 * @param $index 帧索引
		 */
		private function getFrameName($includeName:Boolean, $index:int):String {
			if (hasName && $includeName){
				//对保存的name的索引进行比较，如果索引正确，就将name写入当前帧中
				for (var _frameName:String in namesIndex){
					if (namesIndex[_frameName] == $index)
						return _frameName;
				}
			}
			return null;
		}

		public function toString():String {
			return 'org.zengrong.display.spritesheet::SpriteSheetMetadata{' + 'type:' + type + ',hasLabel:' + hasLabel + ',hasName:' + hasName + ',totalFrame:' + totalFrame + ',maskType:' + maskType + ',labels:' + ObjectUtil.array2String(labels) + ',labelsFrame:' + ObjectUtil.obj2String(labelsFrame) + ',frameRects:' + ObjectUtil.array2String(frameRects) + ',names:' + ObjectUtil.array2String(names) + ',namesIndex:' + ObjectUtil.obj2String(namesIndex) + '}';
		}
	}
}